﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

public enum CreateType
{
    script = 1,                                  //脚本
    config = 2,                                  //所有json表
    texture2D = 3,                               //图片
    textAsset = 4,                               //文本文件
    audioClip = 5,                               //声音文件
    physicMaterial = 6,                          //物理材质
    cubemap = 7,                                 //天空盒
    font = 8,                                    //ttf字体
    shader = 9,                                  //着色器
    material = 10,                               //材质
    uiAtlas = 11,                                //ngui图集
    uiFont = 12,                                 //ngui字体
    gameObject = 13,                             //预设体
    renderTexture = 14,                          //渲染纹理
}

public abstract class CreateABBase
{
    /// <summary>
    /// 打包资源类型
    /// </summary>
    public Type Type { get; protected set; }
    public abstract void Init();
    public abstract bool RemoveAsset(Object obj, string path, Type type);
    public abstract bool AddAsset(Object obj, string path, Type type);
    public abstract void Sort();
    public abstract void Create();
}

public class CreateAB<T> : CreateABBase, IComparer<ABInfo> where T : Object
{
    protected Dictionary<string, ABInfo> infoDic;
    protected List<ABInfo> infoList;
    protected bool isType;
    protected int priority;

    public CreateAB(bool isType, int priority)
    {
        this.isType = isType;
        this.priority = priority;
        infoDic = new Dictionary<string, ABInfo>();
        infoList = new List<ABInfo>();
        Type = typeof(T);
    }

    public override void Init() { } 

    public override bool RemoveAsset(Object obj, string path, Type type)
    {
        if (Type != type) { return false; }
        if (ABInfo.allInfoDic.ContainsKey(path) && infoDic.ContainsKey(path)
            && infoList.Contains(ABInfo.allInfoDic[path]))
        {
            infoDic.Remove(path);
            infoList.Remove(ABInfo.allInfoDic[path]);
            ABInfo.allInfoDic.Remove(path);
        }
        return true;
    }

    public override bool AddAsset(Object obj, string path, Type type)
    {
        //验证是否通过资源类型匹配
        if (!isType)
        {
            GameObject go = obj as GameObject;
            if (go == null || go.GetComponent(Type) == null)
            {
                EDebug.Log("资源类型不存在");
                return false;
            }
        }
        else if (Type != type) { return false; }
        if (ABInfo.allInfoDic.ContainsKey(path))
        {
            EDebug.Log("资源重复");
            return true;
        }
        ABInfo info = new ABInfo(path, priority);
        infoList.Add(info);
        infoDic.Add(info.path, info);
        return true;
    }

    public override void Sort()
    {
        //预设存在引用另一预设   需做排序处理
        if (Type == typeof(GameObject))
        {
            string title = "正在处理依赖 " + Type.ToString();
            for (int i = 0; i < infoList.Count; i++)                            //infoList 中存储了resources文件夹下的所有同一种类型物体
            {
                EditorUtility.DisplayProgressBar(title, infoList[i].path, (i + 1f) / infoList.Count);
                string[] depends = infoList[i].Depends;                         //找到该物体的所有依赖
                for (int j = 0; j < depends.Length; j++)                        //遍历该物体的所有依赖
                {
                    if (Path.GetExtension(depends[j]) == ".prefab")             //如果依赖的是个prefab(这一步其实主要是处理prefab的资源公用问题，unity4.x的依赖关系都需要自己进行记录，5的话就不用了，5比4多了资源变更列表)
                    {
                        ABInfo info = null;
                        infoDic.TryGetValue(depends[j], out info);              //获取这个依赖的prefab
                        if (info != null)
                        {
                            info.applyNum++;                                    //将这个被依赖的prefab的引用次数加一（这一步是为了打包优先级，防止资源重复打包，实际加载的时候还是根据依赖关系进行加载）
                            if (infoList[i] != info)                            //将自己与被依赖的prefab对比，如果不是自己（添加依赖关系）
                            {
                                infoList[i].AddChild(info);                     //将被依赖prefab添加到自己的子物体列表（引用列表）
                                info.AddParent(infoList[i]);                    //将自己添加到被依赖prefab的父物体列表（自己的被引用列表）
                            }

                        }
                    }
                }
            }

            infoList.Sort(this);
            title = "正在排序 " + Type.ToString();
            for (int i = 0; i < infoList.Count; i++)
            {
                EditorUtility.DisplayProgressBar(title, infoList[i].path, (i + 1f) / infoList.Count);
                infoList[i].Sort();     //根据引用次数进行排序（为了将引用次数最高的先打包）
            }
        }
    }

    public override void Create()
    {
        string title = "正在打包 " + Type.ToString();
        for (int i = 0; i < infoList.Count; i++)
        {
            EditorUtility.DisplayProgressBar(title, infoList[i].path, (i + 1f) / infoList.Count);
            Create(infoList[i]);
        }
        EditorUtility.ClearProgressBar();
    }

    public void Create(ABInfo abinfo)
    {
        if (Type == typeof(GameObject))           //预设
        {
            if (!abinfo.isBuilded)                 //首先判断要打包的资源是否已经打包了
            {
                abinfo.isBuilded = true;
                BuildPipeline.PushAssetDependencies();          //压入资源依赖
                //先打包依赖
                if (abinfo.childList != null)
                {
                    for (int i = 0; i < abinfo.childList.Count; i++)
                    {
                        Create(abinfo.childList[i]);                  //递归调用，将依赖资源先打包
                    }
                }
                Object obj = AssetDatabase.LoadMainAssetAtPath(abinfo.path);
                BuildPipeline.BuildAssetBundle(obj, null, abinfo.SavePath,
                    ABController.BuildOptions, ABController.BuildTarget);       //（这边的包都是单包，资源策略上可以改进一下，否则加载的时候太消耗IO资源）
                obj = null; EditorUtility.UnloadUnusedAssetsImmediate();
                if (abinfo.parentList != null)                                  //打包被引用列表的资源（这一步放在这里可有可无）
                {
                    for (int i = 0; i < abinfo.parentList.Count; i++)
                    {
                        Create(abinfo.parentList[i]);
                    }
                }
                BuildPipeline.PopAssetDependencies();          //弹出资源依赖
            }
        }
        else                                     //非预设
        {
            Object obj = AssetDatabase.LoadMainAssetAtPath(abinfo.path);
            BuildPipeline.BuildAssetBundle(obj, null, abinfo.SavePath, 
                ABController.BuildOptions, ABController.BuildTarget);
            obj = null; EditorUtility.UnloadUnusedAssetsImmediate();
        }
    }

    public static string GetObjPath(Object obj)
    {
        return AssetDatabase.GetAssetPath(obj);
    }

    public int Compare(ABInfo x, ABInfo y)
    {
        //引用次数越大  越靠前
        if (x.applyNum >= y.applyNum)
        {
            return 1;
        }
        return -1;
    }
}

public class CreateInOneAB<T> : CreateABBase
{
    private string fileName;
    private string cfgFilePath;

    public List<string> list = new List<string>();
    private string suffix = "Cfg.json";

    public CreateInOneAB(bool isType, int priority, string fileName, string cfgFilePath)
    {
        this.fileName = fileName;
        this.cfgFilePath = cfgFilePath;
        Type = typeof(T);
    }

    public override void Init()
    {
        string fullPath = cfgFilePath + fileName + suffix;
        if (File.Exists(Application.dataPath + fullPath))
        {
            TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>("Assets" + fullPath);
            list = JsonConvert.DeserializeObject<List<string>>(textAsset.text);
        }
    }

    public override bool RemoveAsset(Object obj, string path, Type type)
    {
        //if (type == this.Type)
        //{
        //    if (list.Contains(path))
        //    {
        //        list.Remove(path);
        //    }
        //    return true;
        //}
        return false;
    }

    public override bool AddAsset(Object obj, string path, Type type)
    {
        if (type == Type)
        {
            if (!list.Contains(path))
            {
                list.Add(path);
            }
            return true;
        }
        return false;
    }

    public override void Sort() { }

    public override void Create()
    {
        var listStr = JsonConvert.SerializeObject(list, Formatting.Indented);
        string fullPath = Application.dataPath + cfgFilePath + fileName + suffix;

        FileInfo info = new FileInfo(fullPath); info.Directory.Create();
        byte[] bytes = Encoding.Default.GetBytes(listStr);
        File.WriteAllBytes(fullPath, bytes);

        if (list.Count > 0)
        {
            Object[] objs = new Object[list.Count];
            for (int i = 0; i < list.Count; i++)
            {
                objs[i] = AssetDatabase.LoadAssetAtPath(list[i], Type);
            }
            string file = Application.streamingAssetsPath + "/" + fileName + ".uab";
            BuildPipeline.BuildAssetBundle(null, objs, file, ABController.BuildOptions, ABController.BuildTarget);
        }
    }
}

public class ABInfo
{   
    public static Dictionary<string, ABInfo> allInfoDic = new Dictionary<string, ABInfo>();

    private static string[] pathName = new string[1];
    private List<string> abDepends;
    private string[] depends;

    //引用列表
    public List<ABInfo> childList;
    //被引用列表
    public List<ABInfo> parentList;

    public bool isBuilded = false;
    public string path;
    /// <summary>
    /// 引用次数
    /// </summary>
    public int applyNum;
    /// <summary>
    /// 优先级 越小越高
    /// </summary>
    public int priority;

    /// <summary>
    /// 依赖资源路径
    /// </summary>
    public string[] Depends
    {
        get
        {
            if (depends == null)
            {   
                pathName[0] = path;
                depends = AssetDatabase.GetDependencies(pathName);
            }
            return depends;
        }
    }

    /// <summary>
    /// 依赖资源包路径
    /// </summary>
    public List<string> ABDepends
    {
        get
        {
            if (abDepends == null)
            {
                depends = Depends;
                abDepends = new List<string>();
                List<ABInfo> abList = new List<ABInfo>();
                for (int i = 0; i < depends.Length; i++)
                {
                    if (depends[i] != path)
                    {
                        if (allInfoDic.ContainsKey(depends[i]))
                        {
                            abList.Add(allInfoDic[depends[i]]);
                        }
                    }
                }

                if (abList.Count > 1)
                {
                    abList.Sort(DependsSort);
                }

                for (int i = 0; i < abList.Count; i++)
                {
                    abDepends.Add(abList[i].ABPath);
                }
            }
            return abDepends;
        }
    }

    public void AddParent(ABInfo parent)
    {
        if (parentList == null)
        {
            parentList = new List<ABInfo>();
        }
        parentList.Add(parent);
    }

    public void AddChild(ABInfo child)
    {
        if (childList == null)
        {
            childList = new List<ABInfo>();
        }
        childList.Add(child);
    }

    public void Sort()
    {
        if (parentList != null && parentList.Count >1)
        {
            parentList.Sort(SortFun);
        }
        if (childList != null && childList.Count >1)
        {
            childList.Sort(SortFun);
        }
    }

    private static int SortFun(ABInfo x, ABInfo y)
    {
        //引用次数越大  越靠前
        if (x.applyNum >= y.applyNum)
        {
            return 1;
        }
        return -1;
    }

    private int DependsSort(ABInfo x, ABInfo y)
    {
        if(x.priority > y.priority)
        {
            return 1;
        }
        else if(x.priority == y.priority)
        {
            if(x.applyNum < y.applyNum)
            {
                return -1;
            }
            return 1;
        }
        return -1;
    }


    /// <summary>
    /// 资源包路径
    /// </summary>
    public string ABPath 
    {
        get { return PathUtil.ReplaceString(path) + ".uab"; }
    }

    /// <summary>
    /// 保存路径
    /// </summary>
    public string SavePath
    {
        get
        {
            string path = Path.Combine(Application.streamingAssetsPath, PathUtil.ReplaceString(this.path) + ".uab");
            string dirName = Path.GetDirectoryName(path);
            if (!Directory.Exists(dirName))
            {
                Directory.CreateDirectory(dirName);
            }
            return path;
        }
    }
    
    public ABInfo(string path, int priority)
    {
        this.priority = priority;
        this.path = path;
        allInfoDic.Add(this.path, this);
    }
}